package com.ebs.md.fxmonitor.marketmonitor;

/**
 * Perforce Revision:
 *  $File: //depot/MarketData/fxmonitor/main/src/main/java/com/ebs/md/fxmonitor/marketmonitor/OffMarketListener.java $
 *  $Revision: #28 $  $Change: 115040 $  $DateTime: 2012/09/19 13:09:38 $  $Author: Arik.Sher $
 */

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import org.springframework.util.Assert;

import com.ebs.centralrepository.api.Translator;
import com.ebs.commons.comp.IParsedPayload;
import com.ebs.market.monitor.MarketMonitor.EventType;
import com.ebs.market.monitor.MarketMonitor.MarketIdentfier;
import com.ebs.market.monitor.MarketMonitor.OffMarketEvent;
import com.ebs.market.monitor.MarketMonitor.QuoteSide;
import com.ebs.marketdata.dictionary.MatchingType;
import com.ebs.marketdata.dictionary.PriceContext;
import com.ebs.marketdata.dictionary.SimplePrice;
import com.ebs.md.api.OrderSide;
import com.ebs.md.api.Rate;


/**
 * Listening to TOBC (Top Of Book changes).
 * 
 * checks for OffMarket behaviors(and thresholds), and raising events if so.
 * 
 * New Name: SweepAttempt
 * 
 */
public class OffMarketListener
extends AbstractMonitorListenerAndPublisher
{
	// @see OrderOperation
	protected static final String ACTION_OF_NEW_QUOTE = "ADD";
	protected static final int    POS_FIRST  = 0;
	protected static final int    POS_SECOND = 1;
	
	// Binfile Translator
	protected Translator translator;

	// behavioral 
	protected boolean discardWhenNoBothSides = true;
	
	public void init() {
		super.init();
		Assert.notNull(translator, "'translator' should not be null");
	}

	public void setDiscardWhenNoBothSides(boolean discardWhenNoBothSides) {
		this.discardWhenNoBothSides = discardWhenNoBothSides;
	}

	public void setTranslator(Translator translator) {
		this.translator = translator;
	}

	@Override
	protected boolean thresholdExceeds(IParsedPayload payload) {
		
		final String actionName = CommonPayloadParse.getActionName(payload);
		
		if ( !ACTION_OF_NEW_QUOTE.equalsIgnoreCase(actionName) ) {
			return false;
		}
		final MatchingType matchingType = CommonPayloadParse.getMatchingType(payload);
		// solve PTR 14738
		if (    false
			 || MatchingType.Fix.equals(matchingType)
			 || MatchingType.CM .equals(matchingType) 
		) {
			return false;
		}
		
		if ( discardWhenNoBothSides ) {
			// check that have TOP rates at both sides
			final Rate topBid   = CommonPayloadParse.getTopRateBySide(payload, OrderSide.BID);
			final Rate topOffer = CommonPayloadParse.getTopRateBySide(payload, OrderSide.OFFER);
			
			if ( topBid == null || topOffer == null ) {
				return false;
			}
		}
		OrderSide affectedSide = CommonPayloadParse.getOrderSide(payload);
		Rate      orderRate    = CommonPayloadParse.getOrderRate(payload);
		
//		Rate topRate = CommonPayloadParse.getTopRateBySide(payload, affectedSide);
		List<Rate> topRatesList = CommonPayloadParse.getTopRatesListBySide(payload, affectedSide);
		
		// the BestRate is the New Top. 
		Rate bestRate       = safeGet(topRatesList, POS_FIRST);
		// the SecondBest now is the Previous Top.
		Rate secondBestRate = safeGet(topRatesList, POS_SECOND);
		
		// if no second best than not relevant
		if ( bestRate == null || secondBestRate == null ) {
			return false;
		}
		// just in case, testing that the quote is really the best
		if ( !bestRate.equals(orderRate) ) {
			return false;
		}
		
		boolean cont = true;

		// calc absolute diff (because might be diff between offers, and might return negative value...)
		BigDecimal ratesDiff = CommonTools.calcAbsRatesDiff(bestRate, secondBestRate);
		
		String  baseCurrencyIso = CommonPayloadParse. getBaseCurrencyIso(payload);
		String localCurrencyIso = CommonPayloadParse.getLocalCurrencyIso(payload);

		PriceContext priceContext = getPriceContext(baseCurrencyIso, localCurrencyIso);
		
		cont = (priceContext!=null);
		
		// checking pips threshold
		if ( cont ) {
			final int pipsThreshold  = getPipsThreshold(baseCurrencyIso, localCurrencyIso);
			BigDecimal rateThreshold = getRateByPipCount(pipsThreshold, priceContext);
			
			cont = CommonTools.isGreaterEqual(ratesDiff, rateThreshold);
		}

		// checking amount threshold
		if ( cont ) {
			final long orderTotalAmount = CommonPayloadParse.getOrderTotalAmount(payload);
			final long amountThreshold  = getAmountThreshold(baseCurrencyIso, localCurrencyIso);
			
			cont = (orderTotalAmount >= amountThreshold);
		}
		
		boolean retVal = cont;
//		retVal = true; // for debuging only!!!!

		return retVal;
	}

	protected static <T> T safeGet(List<T> list, int pos) {
		if ( list == null || list.isEmpty() ) {
			return null;
		}
		if ( pos > (list.size() - 1) ) {
			return null;
		}
		
		return list.get(pos);
	}
	
	protected EventMessageCreator getCreator(IParsedPayload payload) {
		String  baseCurrencyIso = CommonPayloadParse. getBaseCurrencyIso(payload);
		String localCurrencyIso = CommonPayloadParse.getLocalCurrencyIso(payload);

		int  pipsThreshold   =   getPipsThreshold(baseCurrencyIso, localCurrencyIso);
		long amountThreshold = getAmountThreshold(baseCurrencyIso, localCurrencyIso);
		
		return new OffMarketJmsMessageCreator(payload, pipsThreshold, amountThreshold);
	}

	/**
	 * 
	 * @param baseCurrencyIso
	 * @param localCurrencyIso
	 * @return if not found than null
	 */
	protected PriceContext getPriceContext(String baseCurrencyIso, String localCurrencyIso) {
		String pairISO = baseCurrencyIso + "/" + localCurrencyIso;
		PriceContext priceContext = translator.getPriceContext(pairISO);
		
		if ( priceContext == null ) {
			logger.warn(String.format("Could not find PriceContext for pair '%s' (via translator).", pairISO));
		}
		return priceContext;
	}

	/**
	 * 
	 * @param pipCount
	 * @param priceContext
	 * @return
	 */
	protected BigDecimal getRateByPipCount(int pipCount, PriceContext priceContext) {
		final SimplePrice rateByPipCountSp = priceContext.pipsUp(new BigDecimal(0), pipCount);
		
		return rateByPipCountSp.asBigDecimal();
	}
	
	protected int getPipsThreshold(String baseCurrencyIso, String localCurrencyIso) {
		if ( eventConfigProvider != null ) {
			return eventConfigProvider.getOffMarketPipsThreshold(baseCurrencyIso, localCurrencyIso);
		}
		return EVENT_OFF_MARKET_DEFAULT_PIPS_THRESHOLD;
	}
	
	protected long getAmountThreshold(String baseCurrencyIso, String localCurrencyIso) {
		if ( eventConfigProvider != null ) {
			return eventConfigProvider.getOffMarketAmountThreshold(baseCurrencyIso, localCurrencyIso);
		}
		return EVENT_OFF_MARKET_DEFAULT_QUOTE_AMOUNT_THRESHOLD;
	}
	
	static class OffMarketJmsMessageCreator 
	extends AbstractEventMessageCreator<OffMarketEvent>
	{
		protected int   pipsThreshold;
		protected long  amountThreshold;
		
		public OffMarketJmsMessageCreator(IParsedPayload payload, int pipsThreshold, long amountThreshold) {
			super(payload);
			this.pipsThreshold   = pipsThreshold;
			this.amountThreshold = amountThreshold;
		}

		public EventType getEventType() {
			return EventType.OFF_MARKET;
		}

		/**
		 * @see com.ebs.md.fxmonitor.orders.TopOfBooksChangeListener#createBasicNotificationPayload
		 * @see com.ebs.md.fxmonitor.orders.TopOfBooksChangeListener#createNotificationPayloadByOrder
		 */
		public OffMarketEvent createEventObject(IParsedPayload payload) {
			OffMarketEvent.Builder builder = OffMarketEvent.newBuilder();
		
			MarketIdentfier.Builder miBuilder = MarketIdentfier.newBuilder();
			miBuilder. setBaseCurrencyIso(payload.getParsedStringField( "base_ccy_iso"));
			miBuilder.setLocalCurrencyIso(payload.getParsedStringField("local_ccy_iso"));
			miBuilder.setMatchingType(    payload.getParsedIntField(   "matching_type"));
			
			builder.setMarketId(miBuilder.build());
			builder.setEventTime(eventTimeMillis);
			
			final String quoteId = payload.getParsedStringField  ("order_id");
			builder.setQuoteId(quoteId);
			QuoteSide quoteSide = CommonTools.getQuoteSide(
					           payload.getParsedIntField   ("qte_type"));
			builder.setQuoteSide(quoteSide);
			Date actionDate =  payload.getParsedDateField  ("action_time");
			builder.setQuoteTime(actionDate.getTime());
			builder.setQuoteRate(payload.getParsedStringField(  "order_rate_str"));
			builder.setQuoteAmount(payload.getParsedLongField(  "order_total_amount"));
			
			builder.  setBidRate(payload.getParsedStringField(  "bid_top_rate_str"));
			builder.setOfferRate(payload.getParsedStringField("offer_top_rate_str"));
			
			OrderSide affectedSide = CommonPayloadParse.getOrderSide(payload);
			List<Rate> topRatesList = CommonPayloadParse.getTopRatesListBySide(payload, affectedSide);
			Rate secondBestRate = safeGet(topRatesList, POS_SECOND);

			if ( secondBestRate != null ) {
				builder.setSecondBestRate(secondBestRate.toString());
			} else {
				builder.setSecondBestRate("");
			}
			
			String dealCode = payload.getParsedStringField("deal_code", null);
			if ( dealCode != null ) {
				builder.setDealCode(dealCode);
			}
			String traderId = payload.getParsedStringField("trader_id", null);
			if ( traderId != null ) {
				builder.setTraderId(traderId);
			}

			String eventId = genEventId(quoteId, actionDate.getTime());
			builder.setEventId(eventId);

			builder.setPipThreshold(String.valueOf(pipsThreshold));
			builder.setAmountThreshold(amountThreshold);
			
			OffMarketEvent built = builder.build();
			return built;
		}
		
	}

}
